/*
 * Copyright (c) 2025 Huawei Technologies Co., Ltd.
 * This file is a part of the CANN Open Software.
 * Licensed under CANN Open Software License Agreement Version 1.0 (the "License").
 * Please refer to the License for details. You may not use this file except in compliance with the License.
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
 * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
 * See LICENSE in the root of the software repository for the full text of the License.
 */

// By setting the K_MAX_SHAPE_DIM macro, the dimension of the AscendC Tensor's ShapeInfo is configured to 0,
// optimizing stack space. If you need to use the ShapeInfo of the AscendC Tensor, please undefine this macro.
#ifndef K_MAX_SHAPE_DIM
#define K_MAX_SHAPE_DIM 0
#endif

#include <iostream>
#include <vector>

#include "helper.hpp"
#include "golden.hpp"
#include "fp16_t.h"

#include "catlass/catlass.hpp"
#include "catlass/arch/arch.hpp"
#include "catlass/gemm/block/block_mmad.hpp"
#include "catlass/gemm/block/block_swizzle.hpp"
#include "catlass/gemm/dispatch_policy.hpp"
#include "catlass/gemm/kernel/matmul_fault_tolerance.hpp"
#include "catlass/gemm/kernel/grouped_matmul_slice_m.hpp"
#include "catlass/gemm/gemm_type.hpp"
#include "catlass/gemv/tile/tile_fault_copy.hpp"
#include "catlass/gemv/tile/tile_fault_vmad.hpp"
#include "catlass/gemv/tile/tile_fault_sum.hpp"
#include "catlass/gemv/tile/tile_vmuls.hpp"
#include "catlass/gemv/block/block_gemv.hpp"
#include "catlass/gemv/kernel/kernel_gemv_aiv.hpp"
#include "catlass/layout/layout.hpp"

#include "catlass/status.hpp"
#include "catlass/gemm/device/device_gemm.hpp"

#include <fstream> // <-- 添加这一行来定义 std::ofstream
#include <iomanip> // <-- 添加这一行来定义 std::setprecision

using namespace Catlass;
using fp16_t = op::fp16_t;

struct Options
{
    const std::string HELPER = "03_matmul_fault m n k check_sum_type [device_id]";

    GemmCoord problemShape{4096, 4096, 4096};
    int32_t checkSumType = 2;
    int32_t deviceId{0};

    Options() = default;

    int Parse(int argc, const char **argv)
    {
        enum ArgsIndex
        {
            M_INDEX = 1,
            N_INDEX,
            K_INDEX,
            CHECKSUMTYPE_INDEX,
            DEVICE_ID_INDEX,
            ARGS_MAX
        };

        if (argc > ARGS_MAX || argc <= CHECKSUMTYPE_INDEX)
        {
            std::cerr << HELPER << std::endl;
            return -1;
        }

        problemShape.m() = std::atoi(argv[M_INDEX]);
        problemShape.n() = std::atoi(argv[N_INDEX]);
        problemShape.k() = std::atoi(argv[K_INDEX]);
        checkSumType = std::atoi(argv[CHECKSUMTYPE_INDEX]);
        if (argc == ARGS_MAX)
        {
            deviceId = std::atoi(argv[DEVICE_ID_INDEX]);
        }
        return 0;
    }
};

template <typename T>
void save_matrix_to_csv_aligned(const std::string &filename,
                                const std::vector<T> &data,
                                size_t rows,
                                size_t cols,
                                int column_width = 18)
{ // 设置一个默认的列宽，可以按需调整
    if (data.size() != rows * cols)
    {
        std::cerr << "错误：数据大小 (" << data.size()
                  << ") 与矩阵维度 (" << rows << "x" << cols << ") 不匹配！" << std::endl;
        return;
    }

    std::ofstream outfile(filename);
    if (!outfile.is_open())
    {
        std::cerr << "错误：无法打开文件 " << filename << " 进行写入！" << std::endl;
        return;
    }

    outfile << std::fixed << std::setprecision(8);

    for (size_t i = 0; i < rows; ++i)
    {
        for (size_t j = 0; j < cols; ++j)
        {
            size_t index = i * cols + j;

            // ==========================================================
            // == 核心改动在这里！==
            // 在输出每个数字之前，使用 setw 设置其固定宽度。
            // ==========================================================
            outfile << std::setw(column_width);
            outfile << static_cast<float>(data[index]);

            // 如果不是当前行的最后一个元素，就添加一个逗号
            if (j < cols - 1)
            {
                outfile << ",";
            }
        }
        outfile << "\n";
    }

    std::cout << "成功将 " << rows << "x" << cols << " 矩阵保存到对齐的 CSV 文件: " << filename << std::endl;
}

void Run(Options const &options)
{
    aclrtStream stream{nullptr};

    ACL_CHECK(aclInit(nullptr));
    ACL_CHECK(aclrtSetDevice(options.deviceId));
    ACL_CHECK(aclrtCreateStream(&stream));

    uint32_t m = options.problemShape.m();
    uint32_t n = options.problemShape.n();
    uint32_t k = options.problemShape.k();

    // Compute the length of each matrix and the size of each buffer
    size_t lenA = static_cast<size_t>(m) * k;
    size_t lenB = static_cast<size_t>(k) * n;
    size_t lenC = static_cast<size_t>(m) * n;
    size_t lenD = lenC;

    size_t sizeA = lenA * sizeof(fp16_t);
    size_t sizeB = lenB * sizeof(fp16_t);
    size_t sizeC = lenC * sizeof(fp16_t);
    size_t sizeD = lenD * sizeof(fp16_t);

    // Define the layout of each matrix
    using LayoutA = layout::RowMajor;
    using LayoutB = layout::RowMajor;
    using LayoutC = layout::RowMajor; // must be rowmajor
    using LayoutX = layout::VectorLayout;
    using LayoutY = layout::VectorLayout;
    LayoutA layoutA{m, k};
    LayoutB layoutB{k, n};
    LayoutC layoutC{m, n};

    // Prepare input data A, B, and X
    std::vector<fp16_t> hostA(lenA, 1.0f);
    std::vector<fp16_t> hostB(lenB, 1.0f);
    std::vector<fp16_t> hostC(lenC);
    std::vector<fp16_t> hostD(lenC, 0.0f);
    golden::FillRandomData<fp16_t>(hostA, -1.0f, 1.0f);
    golden::FillRandomData<fp16_t>(hostB, -1.0f, 1.0f);

    save_matrix_to_csv_aligned("debug_hostA_aligned.csv", hostA, m, k, 15);
    save_matrix_to_csv_aligned("debug_hostB_aligned.csv", hostB, k, n, 15);
    // golden::FillNormalData<float>(hostA, 0.0f, 10.0f);
    // golden::FillNormalData<float>(hostB, 0.0f, 10.0f);

    // Allocate device memory and copy data from host to device
    uint8_t *deviceA{nullptr};
    ACL_CHECK(aclrtMalloc(reinterpret_cast<void **>(&deviceA), sizeA, ACL_MEM_MALLOC_HUGE_FIRST));
    ACL_CHECK(aclrtMemcpy(deviceA, sizeA, hostA.data(), sizeA, ACL_MEMCPY_HOST_TO_DEVICE));

    uint8_t *deviceB{nullptr};
    ACL_CHECK(aclrtMalloc(reinterpret_cast<void **>(&deviceB), sizeB, ACL_MEM_MALLOC_HUGE_FIRST));
    ACL_CHECK(aclrtMemcpy(deviceB, sizeB, hostB.data(), sizeB, ACL_MEMCPY_HOST_TO_DEVICE));

    uint8_t *deviceC{nullptr};
    ACL_CHECK(aclrtMalloc(reinterpret_cast<void **>(&deviceC), sizeC, ACL_MEM_MALLOC_HUGE_FIRST));

    uint8_t *deviceD{nullptr};
    ACL_CHECK(aclrtMalloc(reinterpret_cast<void **>(&deviceD), sizeD, ACL_MEM_MALLOC_HUGE_FIRST));
    ACL_CHECK(aclrtMemcpy(deviceD, sizeD, hostD.data(), sizeD, ACL_MEMCPY_HOST_TO_DEVICE));

    // Prepare FFTS address
    uint64_t fftsAddr{0};
    uint32_t fftsLen{0};
    RT_CHECK(rtGetC2cCtrlAddr(&fftsAddr, &fftsLen));

    // Get the number of cube cores of the current hardware
    auto aicCoreNum = platform_ascendc::PlatformAscendCManager::GetInstance()->GetCoreNumAic();

    // Define ArchTag
    using ArchTag = Arch::AtlasA2;

    // Block level, define BlockMmad
    constexpr bool enableUnitFlag = true;
    using MmadDispatchPolicy = Gemm::MmadAtlasA2Pingpong<enableUnitFlag>;
    using L1TileShape = GemmShape<128, 128, 128>;
    using L0TileShape = GemmShape<128, 128, 64>;
    using AType = Gemm::GemmType<half, LayoutA>;
    using BType = Gemm::GemmType<half, LayoutB>;
    using CType = Gemm::GemmType<half, LayoutC>;
    using BlockMmad = Gemm::Block::BlockMmad<MmadDispatchPolicy, L1TileShape, L0TileShape, AType, BType, CType>;

    // Block level, define sum and Gemv
    using GemvDispatchPolicy = Gemm::GemvAtlasA2;
    using XType = Gemm::GemmType<half, LayoutX>;
    using YType = Gemm::GemmType<half, LayoutY>;
    using TileVmuls = Gemv::Tile::TileVmuls<ArchTag, XType>;

    using LayoutTransposeA = typename std::conditional<
        std::is_same<LayoutA, layout::RowMajor>::value,
        layout::ColumnMajor,
        layout::RowMajor>::type;
    using TransposeAType = Gemm::GemmType<half, LayoutTransposeA>;
    using UBTileShapeTransposeA = GemvShape<L1TileShape::N, L1TileShape::M>;
    using TileFaultCopyTransposeA = Gemv::Tile::TileFaultCopyGemvAiv<ArchTag, TransposeAType, XType, YType>;
    using TileFaultVmadTransposeA = Gemv::Tile::TileFaultVmad<ArchTag, TransposeAType, XType, YType>;
    using TileFaultSumTransposeA = Gemv::Tile::TileFaultSum<ArchTag, TransposeAType, YType>;
    using BlockSumGemvTransposeA = Gemv::Block::BlockSumGemv<GemvDispatchPolicy, UBTileShapeTransposeA, TransposeAType, XType, YType, void, TileFaultCopyTransposeA, TileFaultVmadTransposeA, TileFaultSumTransposeA, TileVmuls>;

    using UBTileShapeB = GemvShape<L1TileShape::M, L1TileShape::N>;
    using TileFaultCopyB = Gemv::Tile::TileFaultCopyGemvAiv<ArchTag, BType, XType, YType>;
    using TileFaultVmadB = Gemv::Tile::TileFaultVmad<ArchTag, BType, XType, YType>;
    using TileFaultSumB = Gemv::Tile::TileFaultSum<ArchTag, BType, YType>;
    using BlockSumGemvB = Gemv::Block::BlockSumGemv<GemvDispatchPolicy, UBTileShapeB, BType, XType, YType, void, TileFaultCopyB, TileFaultVmadB, TileFaultSumB, TileVmuls>;

    using LayoutTransposeC = typename std::conditional<
        std::is_same<LayoutC, layout::RowMajor>::value,
        layout::ColumnMajor,
        layout::RowMajor>::type;
    using TransposeCType = Gemm::GemmType<half, LayoutTransposeC>;
    using UBTileShapeTransposeC = GemvShape<L1TileShape::N, L1TileShape::M>;
    using TileFaultCopyTransposeC = Gemv::Tile::TileFaultCopyGemvAiv<ArchTag, TransposeCType, XType, YType>;
    using TileFaultVmadTransposeC = Gemv::Tile::TileFaultVmad<ArchTag, TransposeCType, XType, YType>;
    using TileFaultSumTransposeC = Gemv::Tile::TileFaultSum<ArchTag, TransposeCType, YType>;
    using BlockSumGemvTransposeC = Gemv::Block::BlockSumGemv<GemvDispatchPolicy, UBTileShapeTransposeC, TransposeCType, XType, YType, void, TileFaultCopyTransposeC, TileFaultVmadTransposeC, TileFaultSumTransposeC, TileVmuls>;

    using UBTileShapeC = GemvShape<L1TileShape::M, L1TileShape::N>;
    using TileFaultCopyC = Gemv::Tile::TileFaultCopyGemvAiv<ArchTag, CType, XType, YType>;
    using TileFaultVmadC = Gemv::Tile::TileFaultVmad<ArchTag, CType, XType, YType>;
    using TileFaultSumC = Gemv::Tile::TileFaultSum<ArchTag, CType, YType>;
    using BlockSumGemvC = Gemv::Block::BlockSumGemv<GemvDispatchPolicy, UBTileShapeC, CType, XType, YType, void, TileFaultCopyC, TileFaultVmadC, TileFaultSumC, TileVmuls>;

    if (m > n)
    {
        // Define BlockScheduler
        // Swizzle offset is 3 and direction is 0.
        using BlockScheduler = typename Gemm::Block::GemmIdentityBlockSwizzle<3, 0>;
        // Kernel level
        using MatmulKernel = Gemm::Kernel::MatmulFaultTolerance<BlockMmad, BlockSumGemvTransposeA, BlockSumGemvB, BlockSumGemvTransposeC, BlockSumGemvC, BlockScheduler>;
        // Prepare params
        typename MatmulKernel::Arguments arguments{
            options.problemShape, deviceA, deviceB, deviceC, deviceD, options.checkSumType};
        using MatmulAdapter = Gemm::Device::DeviceGemm<MatmulKernel>;
        MatmulAdapter matmul_op;
        size_t sizeWorkspace = matmul_op.GetWorkspaceSize(arguments);
        uint8_t *deviceWorkspace{nullptr};
        if (sizeWorkspace > 0)
        {
            ACL_CHECK(
                aclrtMalloc(reinterpret_cast<void **>(&deviceWorkspace), sizeWorkspace, ACL_MEM_MALLOC_HUGE_FIRST));
        }
        matmul_op.Initialize(arguments, deviceWorkspace);

        matmul_op(stream, aicCoreNum, fftsAddr);

        ACL_CHECK(aclrtSynchronizeStream(stream));
        if (sizeWorkspace > 0)
        {
            ACL_CHECK(aclrtFree(deviceWorkspace));
        }

        // Copy the result from device to host
        ACL_CHECK(aclrtMemcpy(hostC.data(), sizeC, deviceC, sizeC, ACL_MEMCPY_DEVICE_TO_HOST));
    }
    else
    {
        // Define BlockScheduler
        // Swizzle offset is 3 and direction is 1.
        using BlockScheduler = typename Gemm::Block::GemmIdentityBlockSwizzle<3, 1>;
        // Kernel level
        using MatmulKernel = Gemm::Kernel::MatmulFaultTolerance<BlockMmad, BlockSumGemvTransposeA, BlockSumGemvB, BlockSumGemvTransposeC, BlockSumGemvC, BlockScheduler>;
        // Prepare params
        typename MatmulKernel::Arguments arguments{
            options.problemShape, deviceA, deviceB, deviceC, deviceD, options.checkSumType};
        using MatmulAdapter = Gemm::Device::DeviceGemm<MatmulKernel>;
        MatmulAdapter matmul_op;
        size_t sizeWorkspace = matmul_op.GetWorkspaceSize(arguments);
        uint8_t *deviceWorkspace{nullptr};
        if (sizeWorkspace > 0)
        {
            ACL_CHECK(
                aclrtMalloc(reinterpret_cast<void **>(&deviceWorkspace), sizeWorkspace, ACL_MEM_MALLOC_HUGE_FIRST));
        }
        matmul_op.Initialize(arguments, deviceWorkspace);

        matmul_op(stream, aicCoreNum, fftsAddr);
        ACL_CHECK(aclrtSynchronizeStream(stream));
        // Copy the result from device to host
        ACL_CHECK(aclrtMemcpy(hostC.data(), sizeC, deviceC, sizeC, ACL_MEMCPY_DEVICE_TO_HOST));
        ACL_CHECK(aclrtMemcpy(hostD.data(), sizeC, deviceD, sizeC, ACL_MEMCPY_DEVICE_TO_HOST));

        int eTA_index = 0, Be_index = 0, eTAB_index = 0, ABe_index = 0, eTC_index = 0, Ce_index = 0;
        if (options.checkSumType == 1)
        { // COL_CHECKSUM
            eTA_index = (m + L1TileShape::M - 1) / L1TileShape::M * k;
            Be_index = eTA_index;
            eTAB_index = Be_index + (((m + L1TileShape::M - 1) / L1TileShape::M) * ((n + L1TileShape::N - 1) / L1TileShape::N)) * L1TileShape::N;
            ABe_index = eTAB_index;
            eTC_index = ABe_index + (((m + L1TileShape::M - 1) / L1TileShape::M) * ((n + L1TileShape::N - 1) / L1TileShape::N)) * L1TileShape::N;
            Ce_index = eTC_index;

            for (int i = 0; i < eTAB_index - Be_index; ++i)
            {
                if (std::abs(hostD[Be_index + i] - hostD[ABe_index + i]) > 0.02)
                {
                    printf("hostEAB[%d] = %f, hostEC[%d] = %f\n", i, static_cast<float>(hostD[Be_index + i]), i, static_cast<float>(hostD[ABe_index + i]));
                }
            }
        }
        else if (options.checkSumType == 2)
        { // ROW_CHECKSUM
            eTA_index = 0;
            Be_index = eTA_index + (n + L1TileShape::N - 1) / L1TileShape::N * k;
            eTAB_index = Be_index;
            ABe_index = eTAB_index + (((m + L1TileShape::M - 1) / L1TileShape::M) * ((n + L1TileShape::N - 1) / L1TileShape::N)) * L1TileShape::M;
            eTC_index = ABe_index;
            Ce_index = eTC_index + (((m + L1TileShape::M - 1) / L1TileShape::M) * ((n + L1TileShape::N - 1) / L1TileShape::N)) * L1TileShape::M;

            for (int i = 0; i < ABe_index - eTAB_index; ++i)
            {
                if (std::abs(hostD[eTAB_index + i] - hostD[eTC_index + i]) > 0.1)
                {
                    printf("hostABE[%d] = %f, hostCE[%d] = %f\n", i, static_cast<float>(hostD[eTAB_index + i]), i, static_cast<float>(hostD[eTC_index + i]));
                }
            }
        }
        else if (options.checkSumType == 3)
        { // ALL_CHECKSUM NOT SUPPORTED YET
          // eTA_index = (m + L1TileShape::M - 1) / L1TileShape::M * k;
          // Be_index =  eTA_index + (n + L1TileShape::N - 1) / L1TileShape::N * k;
          // eTAB_index = Be_index + (((m + L1TileShape::M - 1) / L1TileShape::M) * ((n + L1TileShape::N - 1) / L1TileShape::N)) * L1TileShape::N;
          // ABe_index = eTAB_index + (((m + L1TileShape::M - 1) / L1TileShape::M) * ((n + L1TileShape::N - 1) / L1TileShape::N)) * L1TileShape::M;
          // eTC_index = ABe_index + (((m + L1TileShape::M - 1) / L1TileShape::M) * ((n + L1TileShape::N - 1) / L1TileShape::N)) * L1TileShape::N;
          // Ce_index = eTC_index + (((m + L1TileShape::M - 1) / L1TileShape::M) * ((n + L1TileShape::N - 1) / L1TileShape::N)) * L1TileShape::M;

            // for(int i = 0; i < eTAB_index - Be_index; ++i) {
            //     if(abs(hostD[Be_index + i] - hostD[ABe_index + i]) > 0.02) {
            //         printf("hostEAB[%d] = %f, hostEC[%d] = %f\n", i, hostD[Be_index + i], i, hostD[ABe_index + i]);
            //     }
            // }
            // for(int i = 0; i < ABe_index - eTAB_index; ++i) {
            //     if(abs(hostD[eTAB_index + i] - hostD[eTC_index + i]) > 0.02) {
            //         printf("hostABE[%d] = %f, hostCE[%d] = %f\n", i, hostD[eTAB_index + i], i, hostD[eTC_index + i]);
            //     }
            // }
        }

        printf("checkSumType: %d, eTA_index: %d, Be_index: %d, eTAB_index: %d, ABe_index: %d, eTC_index: %d, Ce_index: %d\n",
               options.checkSumType, eTA_index, Be_index, eTAB_index, ABe_index, eTC_index, Ce_index);

        // // When A and B is filled with 1.0f, use these lines to check the value of each checksum
        //
        // float eTA_value = L1TileShape::M, Be_value = L1TileShape::N, eTAB_value = L1TileShape::M * k, ABe_value = L1TileShape::N * k, eTC_value = eTAB_value, Ce_value = ABe_value;
        // printf("eTA_value: %f, Be_value: %f, eTAB_value: %f, ABe_value: %f, eTC_value: %f, Ce_value: %f\n",
        //     eTA_value, Be_value, eTAB_value, ABe_value, eTC_value, Ce_value);
        // for(int i = 0; i < lenD; ++i) {
        //     if(i < eTA_index) {
        //         if(hostD[i] != eTA_value)
        //             printf("%d != %f, %f\n", i, eTA_value, hostD[i]);
        //     } else if(i < Be_index) {
        //         if(hostD[i] != Be_value)
        //             printf("%d != %f, %f\n", i, Be_value, hostD[i]);
        //     } else if(i < eTAB_index) {
        //         if(hostD[i] != eTAB_value)
        //             printf("%d != %f, %f\n", i, eTAB_value, hostD[i]);
        //     } else if(i < ABe_index) {
        //         if(hostD[i] != ABe_value)
        //             printf("%d != %f, %f\n", i, ABe_value, hostD[i]);
        //     } else if(i < eTC_index) {
        //         if(hostD[i] != eTC_value)
        //             printf("%d != %f, %f\n", i, eTC_value, hostD[i]);
        //     } else if(i < Ce_index) {
        //         if(hostD[i] != Ce_value)
        //             printf("%d != %f, %f\n", i, Ce_value, hostD[i]);
        //     } else if(i >= Ce_index) {
        //         if(hostD[i] != 0.0f)
        //             printf("%d != %f, %f\n", i, 0.0f, hostD[i]);
        //     }
        // }

        for (int i = 0; i < 20; ++i)
        {
            matmul_op(stream, aicCoreNum, fftsAddr);
        }

        int num_repeat = 100;

        aclrtEvent start, stop;
        float temp_time = 0;
        float time = 0;
        ACL_CHECK(aclrtCreateEvent(&start));
        ACL_CHECK(aclrtCreateEvent(&stop));

        for (int i = 0; i < num_repeat; ++i)
        {
            ACL_CHECK(aclrtSynchronizeStream(stream));
            ACL_CHECK(aclrtRecordEvent(start, stream));

            matmul_op(stream, aicCoreNum, fftsAddr);

            ACL_CHECK(aclrtSynchronizeStream(stream));
            ACL_CHECK(aclrtRecordEvent(stop, stream));
            ACL_CHECK(aclrtSynchronizeEvent(stop));
            ACL_CHECK(aclrtEventElapsedTime(&temp_time, start, stop));
            time += temp_time;
        }

        printf("m: %u, n: %u, k: %u, %f TFLOPS, %f ms, repeat: %d\n", m, n, k,
               (float)2 * m * n * k / (time / num_repeat * 1e-3) / 1e12, (time / num_repeat), num_repeat);

        ACL_CHECK(aclrtSynchronizeStream(stream));
        if (sizeWorkspace > 0)
        {
            ACL_CHECK(aclrtFree(deviceWorkspace));
        }
    }

    // Compute the golden result
    std::vector<float> hostGolden(lenC);
    golden::ComputeMatmul(options.problemShape, hostA, layoutA, hostB, layoutB, hostGolden, layoutC);

    // Compare the result
    std::vector<uint64_t> errorIndices = golden::CompareData(hostC, hostGolden, k);
    if (errorIndices.empty())
    {
        std::cout << "Compare success." << std::endl;
    }
    else
    {
        std::cerr << "Compare failed. Error count: " << errorIndices.size() << std::endl;
    }

    ACL_CHECK(aclrtFree(deviceA));
    ACL_CHECK(aclrtFree(deviceB));
    ACL_CHECK(aclrtFree(deviceC));

    ACL_CHECK(aclrtDestroyStream(stream));
    ACL_CHECK(aclrtResetDevice(options.deviceId));
    ACL_CHECK(aclFinalize());
}

int main(int argc, const char **argv)
{
    Options options;
    if (options.Parse(argc, argv) != 0)
    {
        return -1;
    }
    Run(options);
    return 0;
}
